//
//  runtimeViewController.m
//  runtimeStudy
//
//  Created by PF on 2021/12/23.
//

#import "runtimeViewController.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>
#import <mach/mach.h>
#import <pthread/pthread.h>
#import "AFHTTPRequestOperationManager.h"
#import "father.h"
//#import "cOperation.h"
#import "MQTTSessionManager.h"
#import <MQTTClient/MQTTClient.h>
#import <MQTTClient/MQTTLog.h>
NSInteger g = 10;

@interface NSRunLoop(test)

@end
@implementation NSRunLoop(test)

-(void)dealloc
{
    NSLog(@"SNSRunLoop-dealloc");
}

@end


@interface runtimeViewController ()<UITableViewDelegate,UITableViewDataSource,MQTTSessionManagerDelegate,MQTTSessionDelegate>
{
    person *_p;
    NSArray *_dataArray;
    __weak person* _person;
    
    
    dispatch_queue_t queue_serial_custom;
    dispatch_queue_t queue_con_custom;
    dispatch_queue_t queue_con_global;
    dispatch_queue_t targetQueue;
    dispatch_source_t timer;
    
   __weak NSRunLoop *_runloop;
    NSObject *obj1;
    NSLock* _lock;
    
    MQTTSessionManager *_manager;
    MQTTSession *_session;
}
@end

@implementation runtimeViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
//    self.edgesForExtendedLayout = UIRectEdgeNone;
    UITableView *tableView = [[UITableView alloc]initWithFrame:self.view.bounds];
//    tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    tableView.delegate = self;
    tableView.dataSource = self;
    [self.view addSubview:tableView];
    _dataArray = @[@"内存大小",
                   @"kvo",
                   @"kvc",
                   @"block",
                   @"多线程相关（gcd、operation、锁）",
                   @"SDWebImage源码",
                   @"属性关键字copy，与深复制、浅复制",
                   @"load方法与initialize方法的区别和作用",
                   @"归档NSKeyedArchiver与NSCoding协议",
                   @"NSNotification",
                   @"mqtt",
    ];
    
    
    targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);//目标队列
    queue_serial_custom = dispatch_queue_create("queue_serial_custom", DISPATCH_QUEUE_SERIAL);//串行队列
    queue_con_custom = dispatch_queue_create("queue_CONCURRENT_custom", DISPATCH_QUEUE_CONCURRENT);//并发队列
    queue_con_global = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
    //设置参考
    //dispatch_set_target_queue(queue_serial_custom, targetQueue);
    //dispatch_set_target_queue(queue_con_custom, targetQueue);
    
    _lock = [[NSLock alloc]init];
    
    
//    _manager = [[MQTTSessionManager alloc] init];
//    _manager.delegate = self;
//    [_manager connectTo:@"iot.eclipse.org" port:1883 tls:NO keepalive:3000 clean:true auth:NO user:nil pass:nil will:[@"hello world! Disconnect!" dataUsingEncoding:NSUTF8StringEncoding] willTopic:@"MQTTExample/Message" willMsg:nil willQos:MQTTQosLevelExactlyOnce willRetainFlag:YES withClientId:@"ExampleAndroidClient" securityPolicy:nil certificates:nil protocolLevel:MQTTProtocolVersion0 connectHandler:^(NSError *error) {
//    }];
//    [_manager connectTo:@"192.168.1.126" port:18080 tls:NO keepalive:3000 clean:true auth:NO user:nil pass:nil will:[@"hello world! Disconnect!" dataUsingEncoding:NSUTF8StringEncoding] willTopic:@"MQTTExample/Message" willMsg:nil willQos:MQTTQosLevelExactlyOnce willRetainFlag:YES withClientId:@"ExampleAndroidClient" securityPolicy:nil certificates:nil protocolLevel:MQTTProtocolVersion0 connectHandler:^(NSError *error) {
//    }];
//    [_manager addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil];
//    _manager.subscriptions = @{@"MQTTExample/Message":@(MQTTQosLevelExactlyOnce)};
   
    MQTTCFSocketTransport *transport = [[MQTTCFSocketTransport alloc] init];
    transport.host = @"localhost";
    transport.port = 18080;
    MQTTSession *session = [[MQTTSession alloc] init];
    session.transport = transport;
    session.delegate = self;
//    [session setUserName:@""];
//    [session setPassword:@""];
//    session.connectMessage.qos = 2;
//    session.willRetainFlag = NO;
//    session.willFlag = YES;
//    session.willTopic =@"主题内容";
//    NSString*willMsgStr =@"主题信息";
//    NSData *data = [willMsgStr dataUsingEncoding:NSUTF8StringEncoding];
//    session.willMsg= data;
//    session.cleanSessionFlag = true;
    [session addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    session.keepAliveInterval = 30;
    [session connect];
    _session = session;
    
    
}

-(void)test11{
    NSData *data = [@"你好!" dataUsingEncoding:NSUTF8StringEncoding];
    [_session publishData:data onTopic:@"temp" retain:YES qos:MQTTQosLevelExactlyOnce publishHandler:^(NSError *error) {
        NSLog(@"sunao");
    }];
}

#pragma mark MQTTSessionDelegate

- (void)newMessage:(MQTTSession *)session data:(NSData *)data onTopic:(NSString *)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsigned int)mid {
    NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    DDLogInfo(@"data::::%@",str);
}

- (void)session:(MQTTSession *)session newMessage:(NSData *)data onTopic:(NSString *)topic {
    
}

- (BOOL)newMessageWithFeedback:(MQTTSession *)session data:(NSData *)data onTopic:(NSString *)topic qos:(MQTTQosLevel)qos retained:(BOOL)retained mid:(unsigned int)mid {
    return YES;
}

- (void)connected:(MQTTSession *)session {
    
}

- (void)connectionClosed:(MQTTSession *)session {
    
}

- (void)connectionRefused:(MQTTSession *)session error:(NSError *)error {
    
}

- (void)connectionError:(MQTTSession *)session error:(NSError *)error {
    
}

- (void)connected:(MQTTSession *)session sessionPresent:(BOOL)sessionPresent {
    
}

- (void)subAckReceived:(MQTTSession *)session msgID:(UInt16)msgID grantedQoss:(NSArray<NSNumber *> *)qoss {
    
}

- (void)handleEvent:(MQTTSession *)session event:(MQTTSessionEvent)eventCode error:(NSError *)error {
    DDLogWarn(@"错误码:%li---%@",(long)eventCode,error.localizedDescription);
    if (error) {
        if (_session.status != MQTTSessionStatusConnected) {
             [_session connect];
        }
    }
}

 
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    switch (_manager.state) {
        case MQTTSessionManagerStateClosed:
            break;
        case MQTTSessionManagerStateClosing:
            break;
        case MQTTSessionManagerStateConnected:
            break;
        case MQTTSessionManagerStateConnecting:
            break;
        case MQTTSessionManagerStateError:
            break;
        case MQTTSessionManagerStateStarting:
        default:
            break;
    }
}




//内存大小、对齐
-(void)test1{
    unsigned long a = class_getInstanceSize([person class]);
    unsigned long b = malloc_size((__bridge const void*)[person new]);
    struct test_struct {
        int age;
        //        char *c;
        int age1;
        int age2;
        int age3;
        int age4;
        int age5;
        int age6;
    };
    struct test_struct sss;
    //    sss.c = "asdf";
    NSLog(@"-----%lu",sizeof(struct test_struct));
    NSLog(@"end");
}

#pragma mark - kvo
//1、动态创建NSKVONotifying_xxx的类
//2、动态替换set方法 为 Foundation的_NSSetObjectValueAndNotify，内部调用willChangeValueForKey和didChangeValueForKey来触发回调；其中willChangeValueForKey内部会调用kvc方法取原值，didChangeValueForKey内部调用kvc方法取新值
//3、修改isa、重写class方法
//4、为了避免keypath可能会出错、用NSString *keyPath = NSStringFromSelector(@selector(isXXXX))
//5、add remove成对出现，重复remove、remove没有注册的key、注册后观察者释放了再次接受回调、都会crash；ios11以前被观察者销毁前如果还注册着观察者，会crash
//6、重复注册不会crash，但是会重复执行回调方法
//7、在多线程环境下，回调方法线程和修改属性线程是一致的
//8、options各种值的含义
//9、通过属性set方法触发回调会得到key对应(名字一样)属性的值(对应的实例变量或者是属性别名)，没有类似kvc的查找规则
//10、手动触发回调方法时会得到_key实例变量的值，如果没有就为空
//11、kvc触发回调时，如果走的set方法，那么和通过属性set方法触发是一样的最后都走的NSSetObjectValueAndNotify方法；如果通过kvc回调且并没有走set方法时那么会在kvc方法内部调用willChangeValueForKey和didChangeValueForKey方法来触发回调
-(void)test2{
    _p = [person new];
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    //    [_p addObserver:self forKeyPath:@"ID" options:options context:nil];
    //    Class cls =  object_getClass(_p);
    dispatch_queue_t queue = dispatch_queue_create("ff", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        [_p addObserver:self forKeyPath:@"ID" options:options context:nil];
    });
    dispatch_barrier_async(queue, ^{
        //        _p.ID = @"123";
        [_p willChangeValueForKey:@"ID"];
        //        _p.ID = @"123";
        [_p didChangeValueForKey:@"ID"];
    });
    NSLog(@"test2");
}
//-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
//{
//    NSLog(@"observeValueForKeyPath");
//}
#pragma mark - kvc
//1、动态修改实例变量
//2、set时，优先找setKey _setKey方法、找不到看看类方法（accessInstanceVariablesDirectly）、然后直接访问实例变量 _key,_isKey,key,isKey
//3、找不到key会调用forUndefinedKey并且crash
//4、对于基础变量如果设置nil会调用setNilValueForKey并且crash
//5、get时，getKey、key、isKey、_key方法再实例变量_key,_isKey,key,isKey
-(void)test3{
    _p = [person new];
    [_p addObserver:self forKeyPath:@"abc" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    [_p setValue:@"sunao" forKey:@"abc"];
    NSString* a = [_p valueForKey:@"abc"];
    [_p method1];
}




#pragma mark - block
/**
 block 的内部实现数据结构介绍
 block 的三种类型及其相关的内存管理方式
 * block 如何通过 capture 变量来达到访问函数外的变量
 1.通过__block捕获可读写（内部怎么实现的）
 2.__strong和__weak对变量引用计数的影响、内存管理、循环引用、提前释放
 
 
 __block int age = 10，系统做了哪些？
 编译器会将__block变量包装成一个对象
 struct __Block_byref_age_0 {
 void *__isa;
 __Block_byref_age_0 *__forwarding;//age的地址
 int __flags;
 int __size;
 int age;//age 的值
 };
 
 我们在声明一个block属性的时候，习惯用copy关键字，是为了把栈区的block拷贝到堆区，让我们来管理他的生命周期。ARC环境下会根据情况自动将栈上的block拷贝到堆上。ARC环境下也用copy是为了和MRC环境统一，也可以用strong。
 
 __block修饰对象类型
 当__block变量在栈上时，不会对指向的变量产生强引用。
 当__block变量copy到堆上时，会根据这个变量的修饰符是__strong,__weak,__unsafe_unretained做出相应的操作形成强引用（retain）或者弱引用。
 
 
 
 
 
 
 *自己理解的block内部流程
 1.分为3个时期
 编译时，由llvm编译器前端clang将oc代码翻译成c代码，这时根据runtime源码中的blockLayout结构体去生成block的对应结构体（类似原型模式）结构体前面的数据都是一样的，后面包含block捕获的变量，所以不一样；
 运行时的block定义，代码运行到了block定义的地方了
 运行时的block运行，block执行了
 
 2.编译时，创建了静态的block调用函数、desc结构体、捕获的变量；通过构造方法去生成对应结构体；在这一时期所有的block都是栈类型，因为代码还没运行；
 在构造方法中捕获的过程中__strong成员变量强引用__strong类型变量，__weak成员变量弱引用了__weak类型变量
 对于__block id变量，在对应的__Block_byref结构体中会强引用id变量
 在运行时如果栈block拷贝到了堆，那么会再次强引用__strong类型变量
 
 3.
 */



-(void)test4{
    //    [self block_type];
    //    [self testCopy];
    [self block_reference];
    //    [self block_byref];
    //    [self block_weak];
}

/**
 __NSGlobalBlock__
 位于全局区、不使用自动变量、只使用静态变量和全局变量；全局变量和全局静态变量直接通过变量名访问、不捕获；局部static变量按指针传递、捕获的是地址
 __NSStackBlock__ 位于栈区、捕获了自动变量、不使用__block捕获拷贝值、使用__block捕获自动变量在内部先构造一个栈空间结构体，__forwarding先指向自己，block捕获的是这个结构体的地址
 __NSMallocBlock__ 位于堆区、__NSStackBlock__类型bolck被强引用或者copy操作（都是在内部对block执行copy操作）
 */
-(void)block_type{
    static NSInteger s;
    NSInteger p;
    person *ps = [person new];
    void(^block)(void)=^(){
        NSLog(@"aaaaa");
    };
    void(^block1)(void)=^(){
        s = 2;
        NSLog(@"%ld",s);
    };block1();
    void(^block2)(void)=^(){
        g = 3;
        NSLog(@"%ld",g);
    };block2();
    void(^block3)(void)=^(){
        NSLog(@"%ld",p);
    };
    void(^block4)(void)=^(){
        ps.ID = @"123";
        NSLog(@"%ld",ps);
    };
    block4();
    __weak void(^block5)(void)=^(){
        NSLog(@"%ld",ps);
    };
    NSLog(@"结束");
}

/**
 __block类型变量在内部会生成一个结构体，block在栈上时__forwarding指向栈上的自己，block拷贝到堆时，结构体拷贝到堆上，栈上结构体__forwarding指向堆结构体，堆结构体__forwarding指向堆自己
 */
-(void)block_byref{
    __block NSInteger obj = 0;
    NSLog(@"%p",&obj);
    __weak void(^block)(void)=^(){
        NSLog(@"%p",&obj);
    };
    NSLog(@"%p",&obj);
    block();
    [block copy];
    NSLog(@"%p",&obj);
    block();
}

-(void)block_reference{
    person *obj = [person new];
    NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)obj));
    //   栈block强引用obj
    __weak void(^block)(void)=^(){
        NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)obj));
    };
    block();
    //   栈block强引用obj，拷贝到堆block再次强引用
    __strong void(^block1)(void)=^(){
        NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)obj));
    };
    block1();
}


/**
 __weak涉及到内存管理的weak表知识了
 强引用赋值给__weak对象时，编译器会自动添加代码让__weak对象引用计数加一（不明白为什么，不影响其它的）
 objc_initWeak -> storeWeak -> weak_register_no_lock
 */
-(void)block_weak{
    person *obj = [person new];
    NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)obj));
    __weak person *obj_weak = obj;
    _person = obj;
    NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)obj_weak));
    NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)obj));
    
    person *obj1 = obj;
    NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)obj_weak));
    NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)obj));
    NSLog(@"%ld",(long)CFGetRetainCount((__bridge CFTypeRef)obj1));
}

/**
 1、作为递归调用
 2、作为函数返回值用于工厂模式-(id(^)(void))methodName
 3、内联使用^(void){}()
 4、作为方法参数
 5、作为链式语法
 6、__block捕获自动变量
 */
-(void)testUse{
    
}
/**
 ARC下如何解决block循环引用的问题？
 1.第一种方式：__weak
 防止循环引用 外面用__weak 内部用__strong, 执行block是才会强引用防止self释放
 __weak __typeof(self) weakSelf = self;
 self.testBlock = ^{
 __strong __typeof(weakSelf) strongSelf = weakSelf;
 [strongSelf test];
 });
 2.第二种方式：__unsafe_unretained
 3.第三种方式：__block；缺点是必须把引用对象置位nil，并且要调用该block
 __block Person *person = [[Person alloc] init];
 person.block = ^{
 NSLog(@"age is %d", person.age);
 person = nil;
 };
 person.block();
 */
-(void)test_recycle{
    
}


/**
 1、调用Block的copy实例方法时
 2、Block作为函数返回值返回时（编译器添加copy）
 3、将Block赋值给__strong或__block类型成员变量时（编译器添加copy）
 4、将方法名中含有usingBlock的Cocoa框架方法或GCD的API中传递Block时
 5、手动copy内部执行一次_Block_copy，赋值给强引用内部执行一次_Block_copy，手动copy再赋值给强引用也只执行一次。
 */
-(void)testCopy{
    //不可以 __weak void (^block)(void) = [self getBlock]; block();
    //可以 void (^block)(void) = [self getBlock]; block();
    //可以 [self getBlock]();
    //可以 void (^block)(void) = [[self getBlockArr]objectAtIndex:0]; block();
    __block void (^block)(void) = [self getBlock]; block();
}
-(void(^)(void))getBlock
{
    int i = 0;
    return ^(){
        NSLog(@"%d",i);
    };
}
//clang -rewrite-objc main.m
//clang -rewrite-objc -fobjc-arc -fobjc-runtime=macosx-12.1 main.m



#pragma mark - 多线程

-(void)test5{
//    [self test_mult_thread_2];
//    [self test_gcd];
//    [self testThreadNums];
    [self testOp];
}

-(void)testThreadNums{
    dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
    dispatch_queue_t queue1 = dispatch_queue_create(@"123", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue3 = dispatch_queue_create(@"12345", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"start-queue-ssssssss3-%@",[NSThread currentThread]);
    });
//    for(int i = 0; i< 100; ++i) {
//        dispatch_async(queue, ^{
//            NSLog(@"start-queue-ccccccc-%@----%d",[NSThread currentThread],i);
//            sleep(5);
//        });
//    }
    
}
#pragma mark -GCD
/**
 GCD
 1.
 GCD用到的queue，无论是自己创建的，或是获取系统的main queue还是global queue，其最终都是落脚于GCD root queue中； 我们可以在代码中的任意位置创建queue，但最终GCD管理的，也不过这12个root queue，这种思路有点类似于命令模式，即分散创建，集中管理；
 有6个优先级和是否支持过载overcommit（支持overcommit的队列在创建队列时无论系统是否有足够的资源都会重新开一个线程）；
 除了上面的12个，就是com.apple.main-thread还有两个内部管理队列com.apple.libdispatch-manager和com.apple.root.libdispatch-manager以及runloop的运行队列；
 **/
 
-(void)testa{
    NSLog(@"--- %@", [NSThread currentThread]);
    do {
    } while (true);
}

-(void)gcd_deadLock
{
    dispatch_async(queue_serial_custom, ^{
        dispatch_sync(queue_serial_custom, ^{
        });
    });
    dispatch_async(queue_serial_custom, ^{
        NSLog(@"1");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"2");
            dispatch_sync(queue_serial_custom, ^{
                NSLog(@"3");
            });
        });
    });
}

-(void)test_gcd{
    return [self gcd_deadLock];
}

- (void)test_target{
     
    dispatch_queue_t queue = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
    dispatch_set_target_queue(queue, queue_con_custom);
    dispatch_sync(queue, ^{
        _runloop = (NSRunLoop *)[NSRunLoop currentRunLoop];
        [_runloop addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
        [_runloop run];
        NSLog(@"--- %@",[NSThread currentThread]);
        NSLog(@"--- %@",_runloop);
    });
    NSLog(@"--- %@",_runloop);
        
 
    
}


static inline void currentThreadInfo(NSString* str)
{
    if (str)
        NSLog(@"---------%@----------",str);
    
    NSThread* thread = [NSThread currentThread];
    mach_port_t machTID = pthread_mach_thread_np(pthread_self());
    NSLog(@"current thread num: %x thread name:%@", machTID,thread.name);
    
    if (str)
        NSLog(@"-------------------");
}
static inline void dumpThreads(NSString* str) {
    
    NSLog(@"---------%@----------",str);
    currentThreadInfo(nil);
    char name[256];
    thread_act_array_t threads = NULL;
    mach_msg_type_number_t thread_count = 0;
    task_threads(mach_task_self(),&threads, &thread_count);
    for (mach_msg_type_number_t i = 0; i < thread_count; i++) {
        thread_t thread = threads[i];
        pthread_t pthread = pthread_from_mach_thread_np(thread);
        pthread_getname_np(pthread, name, sizeof name);
        NSLog(@"mach thread %x: getname: %s", pthread_mach_thread_np(pthread), name);
    }
    NSLog(@"-------------------");
}



 



//在多线程环境中使用 串行队列 可以保证任何是按顺序执行（进入队列的顺序）但是无法确定进入队列的顺序，因为是多线程环境
//异步步执行并行队列 是并发的 不同线程执行
//异步执行串行队列 是按顺序执行 同一线程执行
-(void)test_mult_thread_0{
    dispatch_queue_t queue = queue_serial_custom;
//    queue = queue_con_custom;
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
        dispatch_async(queue, ^{
            NSLog(@"11 %@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:2];
        });
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_async(queue, ^{
            NSLog(@"22 %@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:2];
        });
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_async(queue, ^{
            NSLog(@"33 %@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:2];
        });
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_async(queue, ^{
            NSLog(@"44 %@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:2];
        });
    });
}

//多线程环境下 同步执行并行队列 是并发的 不同线程执行
//同步执行串行队列 是按顺序执行 不同线程执行
-(void)test_mult_thread_1{
    dispatch_queue_t queue = queue_serial_custom;
    queue = queue_con_custom;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_sync(queue, ^{
            NSLog(@"11 %@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:2];
        });
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_sync(queue, ^{
            NSLog(@"22 %@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:2];
        });
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_sync(queue, ^{
            NSLog(@"33 %@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:2];
        });
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_sync(queue, ^{
            NSLog(@"44 %@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:2];
        });
    });
}

-(void)test_mult_thread_2{
    //多线程环境下利用串行队列保证线程安全
    NSThread *thread1 = [[NSThread alloc]initWithBlock:^{
        [self thread_sync1];
    }];
    [thread1 start];
    NSThread *thread2 = [[NSThread alloc]initWithBlock:^{
        [self thread_sync2];
    }];
    [thread2 start];
}
-(void)thread_sync1{
    while (1) {
//        NSLog(@"thread_sync1_aaa");
        dispatch_async(queue_serial_custom, ^{
            NSLog(@"thread_sync1_bbb");
            [NSThread sleepForTimeInterval:2];
            NSLog(@"thread_sync1_ccc");
        });
//        NSLog(@"thread_sync1_ddd");
    }
}
-(void)thread_sync2{
    while (1) {
//        NSLog(@"thread_sync2_aaa");
        dispatch_async(queue_serial_custom, ^{
            NSLog(@"thread_sync2_bbb");
            [NSThread sleepForTimeInterval:2];
            NSLog(@"thread_sync2_ccc");
        });
//        NSLog(@"thread_sync2_ddd");
    }
}







-(void)test2222{
    dispatch_queue_t qq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, qq);
    dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (3 * NSEC_PER_SEC));
    dispatch_source_set_timer(timer, tt, 1.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
    __weak typeof(self) weakSelf = self;
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
    dispatch_resume(timer);
}


-(void)test1111{
    NSPort *aaa;
    NSLog(@"start");
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        
        [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop] run];
    }];
    [thread start];
    
    dispatch_async(queue_con_custom, ^{
        [self performSelector:@selector(testP) withObject:nil afterDelay:1];
        NSLog(@"%@",[NSThread currentThread]);
        NSLog(@"job3 in");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"job3 out");
    });
    //    dispatch_async(queue_con_custom, ^{
    //        NSLog(@"%@",[NSThread currentThread]);
    //        NSLog(@"job2 in");
    //        [NSThread sleepForTimeInterval:1.f];
    //        NSLog(@"job2 out");
    //    });
    NSLog(@"end");
}
-(void)testP{
    NSLog(@"testP");
}

-(void)test123{
    NSLog(@"start");
    dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);;
    NSMutableArray *array = [[NSMutableArray alloc] init];
    for(int i = 0; i< 100; ++i) {
        dispatch_async(queue, ^{
            //            [array addObject:[NSNumber numberWithInt:i]];
            //            NSLog(@"%@---%d",array,i);
            dispatch_sync(self->queue_serial_custom, ^{
                NSLog(@"start---%@",[NSThread currentThread]);
                [array addObject:[NSNumber numberWithInt:i]];
                NSLog(@"end---%@",[NSThread currentThread]);
            });
        });
    }
    NSLog(@"end");
}

-(void)test_dispatch_semaphore_t
{
    dispatch_semaphore_t _semaphore;
    dispatch_queue_t queue_serial_custom;
    _semaphore = dispatch_semaphore_create(0);
    queue_serial_custom = dispatch_queue_create("queue_serial_custom", DISPATCH_QUEUE_SERIAL);
    
    dispatch_block_t block1 = dispatch_block_create(0, ^{
        NSLog(@"aaaaaaaa");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"2222");
            dispatch_semaphore_signal(_semaphore);
        });
        dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
    });
    
    dispatch_async(queue_serial_custom, ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"1111");
            dispatch_block_cancel(block1);
            dispatch_semaphore_signal(_semaphore);
        });
        dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
    });
    
    
    
    dispatch_async(queue_serial_custom, block1);
    
    dispatch_async(queue_serial_custom, ^{
        NSLog(@"3333");
    });
}

-(void)testAf{
    AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:@"http://b2c.tixaapp.com"]];
    [manager GET:@"http://b2c.tixaapp.com/clientInterface/indexName.jsp?appID=79&goodsNum=100" parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id  _Nonnull responseObject) {
        NSLog(@"success");
    } failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error) {
        NSLog(@"failure");
    }];
    [manager GET:@"http://b2c.tixaapp.com/clientInterface/indexName.jsp?appID=1&goodsNum=100" parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id  _Nonnull responseObject) {
        NSLog(@"success");
    } failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error) {
        NSLog(@"failure");
    }];
}


//测试多线程同步的各种方式
-(void)test_dispatch_group_async
{
    
    dispatch_async(queue_con_global, ^{
        sleep(1);
        NSLog(@"1");
    });
    dispatch_async(queue_con_global, ^{
        sleep(1);
        NSLog(@"2");
    });
    dispatch_async(queue_con_global, ^{
        sleep(1);
        NSLog(@"3");
    });
    dispatch_barrier_async(queue_con_global, ^{
        NSLog(@"dispatch_barrier_async");
    });
    dispatch_async(queue_con_global, ^{
        sleep(1);
        NSLog(@"end");
    });
    
    
    //    dispatch_group_t group = dispatch_group_create();
    //
    //    dispatch_group_enter(group);
    //    dispatch_async(queue_con_custom, ^{
    //        sleep(1);
    //        NSLog(@"1");
    //        dispatch_group_leave(group);
    //    });
    
    //    dispatch_group_async(group, queue_con_custom, ^{
    //        sleep(1);
    //        NSLog(@"1");
    //    });
    //    dispatch_group_async(group, queue_con_custom, ^{
    //        sleep(2);
    //        NSLog(@"2");
    //    });
    //    dispatch_group_async(group, queue_con_custom, ^{
    //        sleep(3);
    //        NSLog(@"3");
    //    });
    
    //    dispatch_notify(group, dispatch_get_main_queue(), ^{
    //        NSLog(@"end1111");
    //        sleep(3);
    //        NSLog(@"end");
    //    });
}



-(void)testOp
{
    
//        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
//            NSLog(@"1 %@",[NSThread currentThread]);
//            [NSThread sleepForTimeInterval:1];
//        }];
//        [op addExecutionBlock:^{
//            NSLog(@"2 %@",[NSThread currentThread]);
//            [NSThread sleepForTimeInterval:1];
//        }];
//        [op start];
    
    
//    cOperation *op = [cOperation new];
//    [op start];
    
//    NSOperationQueue* queue = [NSOperationQueue new];
//    [queue addOperation:op];
 
 
    
    //    dispatch_async(queue_con_custom, ^{
    //        NSLog(@"11 %@",[NSThread currentThread]);
    //    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    //        NSLog(@"1 %@",[NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1];
    //    }];
    //    [op addExecutionBlock:^{
    //        NSLog(@"2 %@",[NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1];
    //    }];
    //    [op start];
    //    [op addExecutionBlock:^{
    //        NSLog(@"2 %@",[NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1];
    //    }];
    //    [op addExecutionBlock:^{
    //        NSLog(@"2 %@",[NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1];
    //    }];
    //    [op addExecutionBlock:^{
    //        NSLog(@"2 %@",[NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1];
    //    }];
    //    [op addExecutionBlock:^{
    //        NSLog(@"2 %@",[NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1];
    //    }];
    //    [op addExecutionBlock:^{
    //        NSLog(@"2 %@",[NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1];
    //    }];
    //    [op start];
    //    NSLog(@"22 %@",[NSThread currentThread]);
    //    });
    
    
    //    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    //        NSLog(@"1 %@",[NSThread currentThread]);
    //    }];
    //    [op addExecutionBlock:^{
    //            NSLog(@"2 %@",[NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1];
    //    }];
    //
    //    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    //        NSLog(@"2 %@",[NSThread currentThread]);
    //    }];
    //    [op1 addExecutionBlock:^{
    //            NSLog(@"3 %@",[NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1];
    //    }];
    //    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    //    [queue addOperations:@[op] waitUntilFinished:YES];
    //    [queue addOperation:op1];
    
    
    //    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
    //                // 进行一些 UI 刷新等操作
    //                for (int i = 0; i < 2; i++) {
    //                    [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
    //                    NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
    //                }
    //            }
    //     ];
    
    
//    MyOperation *myop1 = [[MyOperation alloc]init];
//    __weak MyOperation *weako = myop1;
//    myop1.completionBlock = ^{
//        NSLog(@"completionBlock---%@---%@", [NSThread currentThread],weako);
//    };
//    MyOperation *myop2 = [[MyOperation alloc]init];
//    MyOperation *myop3 = [[MyOperation alloc]init];
//    [myop1 addDependency:myop3];
//    [myop2 addDependency:myop3];
//    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//    [queue addOperation:myop1];
//    [queue addOperation:myop2];
//    [queue addOperation:myop3];
//    [myop3 cancel];
    //    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    //        [myop3 cancel];
    //    });
    
    
    
    
    //    SDAsyncBlockOperation *prefetchOperation = [SDAsyncBlockOperation blockOperationWithBlock:^(SDAsyncBlockOperation * _Nonnull asyncOperation) {
    //        NSLog(@"start---%@", [NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:2.0];
    //        NSLog(@"end---%@", [NSThread currentThread]);
    //        [asyncOperation complete];
    //    }];
    //    prefetchOperation.completionBlock = ^{
    //        NSLog(@"completionBlock---%@", [NSThread currentThread]);
    //    };
    
    
    
    //    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    //    queue.maxConcurrentOperationCount = 1;
    //    [queue addOperationWithBlock:^{
    //        NSLog(@"start1---%@", [NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1.0];
    //        NSLog(@"end1---%@", [NSThread currentThread]);
    //    }];
    //    [queue addOperationWithBlock:^{
    //        NSLog(@"start2---%@", [NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1.0];
    //        NSLog(@"end2---%@", [NSThread currentThread]);
    //    }];
    //    [queue addOperationWithBlock:^{
    //        NSLog(@"start3---%@", [NSThread currentThread]);
    //        [NSThread sleepForTimeInterval:1.0];
    //        NSLog(@"end3---%@", [NSThread currentThread]);
    //    }];
    //    NSLog(@"11111");
    //    [queue addOperations:@[[NSBlockOperation blockOperationWithBlock:^{
    //            NSLog(@"start4---%@", [NSThread currentThread]);
    //            [NSThread sleepForTimeInterval:1.0];
    //            NSLog(@"end4---%@", [NSThread currentThread]);
    //    }]] waitUntilFinished:YES];
    //    NSLog(@"2222");
}





















/**
 1. 通知中心对观察者observer是unsafe_unretained引用
 2. iOS 9.0之后NSNotificationCenter不会对一个dealloc的观察者发送消息，dealloc以后会自动remove观察者
 3. post与触发事件是同步的，并且是同一个线程
 4. 线程安全
 */
-(void)test_NSNotification{
}




/**
1.jsc的用法
2.
 
*/
# pragma mark - RN
-(void)test_RN{
    
}



/**
 1. id 和 instancetype的区别：instancetype具备类型检查 ，用作方法返回类型
 2. nil是oc null是c范畴；nil可以发消息，null发消息会crash
 3. NS_UNAVAILABLE、NS_DESIGNED_INITIALIZER；初始化方法有两种，一种是指定，一种
 4. GCD中的block是否需要weakSelf？不使用weakSelf不会导致循环引用，但是使用weakSelf可以判断self是否已经释放，参考YYkithttps://github.com/ibireme/YYKit/issues/41
 5. dispatch_sync不会持有block
 6. nonatomic和atomic使用，原子性，内部使用自旋锁；自能保证读取或者修改的原子性，如果是I++这样的操作就无效了，不能保证线程安全，需要另外加锁
 7. dealloc并不总是在主线程中被调用，，其调用线程为最后一个调用release方法的线程。也就是说,dealloc方法有可能在任何线程被调用
 8. NSDictionary的setvalue和setobject的区别就是setvalue可以设置nil，内部相当于调用了removeobject for key；valueforkey和objectforkey的区别，objectforkey找不到key是返回nil，valueforkey是kvc的方法，会执行valueForUndefinedKey 从而crash，而且valueforkey会过滤key中的“@”字符
 属性
 1. 编译器自动添加@synthesize name = _name;   @dynamic不给属性添加实例变量、set、get方法；
 2. 属性的继承是继承了实例变量和set、get方法，子类使用@synthesize相当于自已拥有了实例变量和set、get方法
 3. 默认：基本数据类型：atomic(加上自旋锁) readwrite assign; 普通OC对象： atomic readwrite strong
 4. weak： 只可以修饰对象，就是指针类型； assign： 可修饰对象和基本数据类型，如果修饰对象，会产生野指针问题，修饰的对象释放后，指针不会自动被置空，此时向对象发消息会崩溃；对block使用copy，从栈区拷贝到堆区
 5. 对于不可变数组：copy后仍然不可变、只复制指针（浅拷贝）、mutableCopy后变为可变数组并且复制内存（深拷贝） 对于可变数组：copy后不可变、复制内存（深拷贝）、mutableCopy后仍为可变数组并且复制内存（深拷贝）
 readonly属性关键字
 1. 本类可以通过实例变量来修改，readonly相当于不自动生成set方法，可以自己在内部定义set方法，就可以修改属性了
 2. 外部可以通过kvc来修改
 3. 子类通过@synthesize来给属性合成新的实例变量,可以修改新的实例变量，但是和父类的实例变量是不同的
 4. 在外部可以给父类添加一个同名扩展可写属性，然后在父类定义set方法，就可以修改父类的只读属性了
 面试题收集
 1. https://www.jianshu.com/c/31a515b57aef
 2. http://www.cocoachina.com/articles/23171
 */


-(void)dealloc{
    NSLog(@"dealloc");
}
 

# pragma mark - tableview
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _dataArray.count;
}
static NSString *cellIdentfier = @"Cell";
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentfier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentfier];
    }
    cell.textLabel.text = [_dataArray objectAtIndex:indexPath.row];
    return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    SEL sel = NSSelectorFromString([NSString stringWithFormat:@"test%ld",(indexPath.row+1)]);
    //解决 编译警告PerformSelector may cause a leak because its selector is unknown
    IMP imp = [self methodForSelector:sel];
    void (*func) (id, SEL) = (void *)imp;
    if ([self respondsToSelector:sel]) {
        func(self, sel);
    }
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 80;
}

@end
